home *** CD-ROM | disk | FTP | other *** search
- /* complain.cpp / error text lookup */
-
- /* error messages in a program can be "keyed;" that is, they can have a */
- /* replacement key that allows us to look up the message and replace the */
- /* text. this module manages error message files, scanning them and storing */
- /* message offsets for quick lookup. users can edit the files to change */
- /* messages without patching the program. this allows easier */
- /* internationalization, too. of course, the "%" arguments must remain in */
- /* the same order so that printf() doesn't choke! that's the job of err_mgr */
- /* (q.v.). */
-
- #include <stdio.h>
- #ifdef __ZTC__
- #include <io.h> /* fseek() */
- #endif
- #include <stdlib.h>
- #include <string.h>
- #include "utils.hpp" /* utility routines */
- #include "errmgr.hpp" /* err_mgr.warn() etc. */
- #include "complain.hpp" /* us */
-
- /* in this implementation keys are stored in a singly linked list. a */
- /* hash table would be much faster, of course. */
-
- class complain_ptr { /* for list of keys */
- public:
- complain_ptr(char *name,long offset,complain_ptr *head);
- ~complain_ptr(void);
- const char *errname(void) const { return _errname; }
- int complaint_text(const char *filename,char *line,int linelen);
- complain_ptr *next; /* singly linked list */
- private:
- char *_errname; /* allocated by caller; we free */
- long foffset; /* where to find its text in file */
- };
-
- /*************************** class complaint_dict ***************************/
-
- /* reads file, returns head of key list if all OK or 0 otherwise */
-
- static complain_ptr *read_complaint_file(const char *filename);
-
- /* searches list of keys; returns 0 if not found. */
-
- static complain_ptr *key(complain_ptr *complain_table,const char *name);
-
- complaint_dict::complaint_dict(const char *filename)
- {
- /* read the complaint file and remember pointers to each error message */
- /* found therein. */
-
- _filename = newstring(filename);
- complain_table = read_complaint_file(_filename);
-
- } /* end of complaint_dict::complaint_dict() */
-
- complaint_dict::~complaint_dict(void)
- {
- /* cleanup - delete all of the offset records in the table. */
-
- complain_ptr *keyptr;
-
- free(_filename);
- while ((keyptr = complain_table) != 0) {
- complain_table = complain_table->next;
- delete keyptr;
- }
-
- } /* end of complaint_dict::~complaint_dict() */
-
- int complaint_dict::key_defined(const char *name) const
- {
- /* return 1 if the key is defined in this dictionary. */
-
- return key(complain_table,name) != 0;
-
- } /* end of complaint_dict::key_defined() */
-
- static complain_ptr *key(complain_ptr *complain_table,const char *name)
- {
- /* look for the key in the list of complain_ptr objects. in a */
- /* production system this would be a hashed table. */
-
- while (complain_table != 0 && strcmp(name,complain_table->errname()))
- complain_table = complain_table->next;
- return complain_table;
-
- } /* end of key() */
-
- int complaint_dict::complaint_text(const char *name,char *line,
- int linelen) const
- {
- /* store the text of the error message indexed by "name" (if found) */
- /* into the buffer. returns a "not found" message in the line if */
- /* something goes wrong. returns 0 on failure. */
-
- complain_ptr *fmt;
-
- fmt = key(complain_table,name);
-
- /* if no key, return a "key not found" message (in English; sorry). */
- /* we assume there are no "%" signs in the key and that there is room */
- /* in the buffer for the extra text. really the buffer should be */
- /* an object that grows as we add text. */
-
- if (fmt == 0) {
- sprintf(line,"Error key \"%s\" not found\n",key);
- return 0; /* failed */
- }
-
- /* if the file has been moved or mangled, say something to that effect. */
-
- if (fmt->complaint_text(_filename,line,linelen)) {
- sprintf(line,"Unable to re-read text for error key \"%s\"\n",key);
- return 0; /* failed */
- }
-
- return 1; /* all OK */
-
- } /* end of complaint_dict::complaint_text() */
-
- /************************** local utility routines **************************/
-
- static complain_ptr *read_complaint_file(const char *filename)
- {
- /* read all of the error keys in the complaint file. this is called */
- /* from the dictionary's constructor, so we have no way to signal an */
- /* error return. if the file isn't found, then no key will exist. */
- /* if an error is found partway through, some keys will exist. in */
- /* either case err_mgr.error() is called (which may cause execution */
- /* to stop, per user request). */
-
- /* line format: "key_name: text" where the text goes to the end of */
- /* the line. leading blanks after the ':' are skipped. if the last */
- /* character is a '\', the following line is added to the text (the */
- /* '\' is removed). */
-
- FILE *complaintfile;
- char line[512];
- long thisoffset;
- char *keyname,*p,*name;
- int all_ok = 1;
- complain_ptr *keyptr,*complain_table = 0;
-
- if ((complaintfile = fopen(filename,"r")) == 0) {
- err_mgr.error("Unable to read error text file %s\n",
- filename);
- return 0; /* in case user wants to continue */
- }
-
- for (;;) { /* exit from within */
- thisoffset = ftell(complaintfile); /* start of line */
- read_continued_line(complaintfile,line,sizeof(line));
- if (strlen(line) == 0) /* EOF? done */
- break;
-
- /* skip blank lines and comments. */
-
- keyname = p = line + skipblanks(line);
- if (!*p || *p == '\n' || *p == '#')
- continue;
- p += skip_ident(p); /* get key name */
-
- /* format errors within the file are considered only warnings; the */
- /* user will want to see all of them and not be asked whether to */
- /* continue after every error! */
-
- if (p == keyname) {
- err_mgr.warn("Missing error key in error text file \"%s\":\n%s",
- filename,line); /* \n in buf */
- all_ok = 0;
- continue;
- }
-
- name = newstring(keyname,p - keyname);
- if (key(complain_table,name) != 0) {
- err_mgr.warn("Duplicate error key in error text file \"%s\":\n%s",
- filename,line); /* \n in buf */
- free(name);
- all_ok = 0;
- continue;
- }
- p += skipblanks(p);
- if (*p != ':') {
- err_mgr.warn("Missing ':' in error text file \"%s\":\n%s",
- filename,line);
- free(name);
- all_ok = 0;
- continue;
- }
-
- /* everything looks good - remember where the key was. */
-
- keyptr = new complain_ptr(name,thisoffset,complain_table);
- complain_table = keyptr;
- } /* end of for(ever) */
-
- fclose(complaintfile);
- if (!all_ok) {
- while ((keyptr = complain_table) != 0) {
- complain_table = complain_table->next;
- delete keyptr;
- }
- return 0;
- }
- return complain_table;
-
- } /* end of read_complaint_file() */
-
- /**************************** class complain_ptr ****************************/
-
- complain_ptr::complain_ptr(char *name,long offset,complain_ptr *head)
- { /* constructor */
- _errname = name; /* allocated for us; we must delete */
- foffset = offset;
- next = head; /* add ourselves to front of list */
-
- } /* end of complain_ptr::complain_ptr() */
-
- complain_ptr::~complain_ptr(void) /* destructor */
- {
- free(_errname); /* caller cleans up chain in next */
-
- } /* end of complain_ptr::~complain_ptr() */
-
- int complain_ptr::complaint_text(const char *filename,char *line,int linelen)
- {
- /* re-read the complaint text for the key. note that newlines are left */
- /* in the text so that our caller need not supply a terminating newline. */
-
- /* returns 1 on error (file has disappeared or been edited). this is an */
- /* internal routine, so we depend on our caller to print a useful error */
- /* message. */
-
- FILE *complaintfile;
- char *keyname,*p;
- int newlen;
-
- line[0] = '\0'; /* clear old text */
- if ((complaintfile = fopen(filename,"r")) == 0)
- return 1;
- if (fseek(complaintfile,foffset,SEEK_SET)) {
- fclose(complaintfile);
- return 1;
- }
- read_continued_line(complaintfile,line,linelen);
- fclose(complaintfile);
-
- /* make sure we still have the same key! (file may have been edited) */
-
- keyname = p = line + skipblanks(line);
- p += skip_ident(p); /* get key name */
- if (p == keyname || strncmp(keyname,_errname,strlen(_errname)))
- return 1;
- p += skipblanks(p);
- if (*p != ':')
- return 1;
- ++p; /* skip ':', leading blanks */
- p += skipblanks(p);
-
- /* OK, now we need to return only the error text, not the key at the */
- /* front of it. shift everything over to cover the key. */
-
- newlen = strlen(p);
- memmove(line,p,newlen + 1);
- return 0; /* all OK */
-
- } /* end of complain_ptr::complaint_text() */